package test.file;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.bag.HashBag;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.google.common.io.LineProcessor;

public class ReportAProcessor{
	
	private static final String SRC_PATH = "/src/main/java";
	private static final String REPORT_FILE = "/home/mike/git/JavaSecret/Test/src/main/resources/a.txt";
	public static  final String ELEMENT_TYPE = "elementType";
	public static  final String ELEMENT_TYPE_CONSTANT = "Constant";
	public static  final String ELEMENT_TYPE_METHOD = "Method";
	public static  final String LINE_NO = "lineNo";
	public static  final String FULL_NAME = "fullName";
	public static  final String METHOD_NAME = "methodName";
	public static  final String RELATIVE_FILE_PATH = "filePath";
	
	public  void processReport(String encoding, String rootPath, String projectName) throws IOException, Exception {
		rootPath = rootPath+"/"+projectName+SRC_PATH;
		// 读取报告
		System.out.println(">>>>>>>>>> Read Report <<<<< <<<<<");
		File reportFile = new File(REPORT_FILE);
		List<Map> readReport = readReport(reportFile, encoding, rootPath);
		// 整理报告信息
		System.out.println(">>>>>>>>>> Process Report <<<<< <<<<<");
		MultiValueMap<String, String> orgnizedReport = orgnizeReport(readReport, rootPath);
		// 计算出每一个文件需要删除的所有行号
		System.out.println(">>>>>>>>>> Calculate Line Numbers Per File <<<<< <<<<<");
		MultiValueMap<String, Integer> lineNosPerFile = calculateLineNosPerFile(orgnizedReport, encoding);
		// 移除无用方法
		System.out.println(">>>>>>>>>> Remove Unused Method <<<<< <<<<<");
		removeUnusedMethod(lineNosPerFile,encoding);
	}

	public  void removeUnusedMethod(MultiValueMap<String, Integer> lineNosPerFile, String encoding) throws IOException {
		Iterator<String> iterator = lineNosPerFile.keySet().iterator();
		while (iterator.hasNext()) {
			String filePath = (String) iterator.next();
			File targetFile = new File(filePath);
			List<String> contents = Files.readLines(targetFile, Charsets.UTF_8);
			List<Integer> needRemoveLineNoList = lineNosPerFile.get(filePath);
			// 重新写文件
			removeByLineNoList(contents, new HashSet<Integer>(needRemoveLineNoList), targetFile, encoding);
		}
	}

	public  MultiValueMap<String, Integer> calculateLineNosPerFile(MultiValueMap<String, String> processReport, String encoding) throws Exception {
		Iterator<String> iterator = processReport.keySet().iterator();
		// 统计每个文件需要删除的行数
		MultiValueMap<String, Integer> needToRemoveLineNosOfFile = new LinkedMultiValueMap<>();
		while (iterator.hasNext()) {
			String fullFilePath = (String) iterator.next();
			// System.out.println(fullFilePath);///**/WarnService.java
			List<String> methodAndLineNoList = processReport.get(fullFilePath);
			for (String methodAndLineNo : methodAndLineNoList) {
				String[] split = methodAndLineNo.split("\\$");
				String method = split[0];
				String lineNo = split[1];
				String elemtnType = split[2];
				List<Integer> findNeedToRemoveLineNos = findNeedToRemoveLineNosOfMethod(new File(fullFilePath), encoding, method, lineNo,elemtnType);
				List<Integer> old = needToRemoveLineNosOfFile.get(fullFilePath);
				List<Integer> newList = new ArrayList<>();
				if(CollectionUtils.isNotEmpty(findNeedToRemoveLineNos)){
					newList.addAll(findNeedToRemoveLineNos);
				}
				if(CollectionUtils.isNotEmpty(old)){
					newList.addAll(old);
				}
				needToRemoveLineNosOfFile.put(fullFilePath, newList);
			}
		}
		Iterator<String> iteratorOfFileLineNos = needToRemoveLineNosOfFile.keySet().iterator();
		while (iteratorOfFileLineNos.hasNext()) {
			String key = (String) iteratorOfFileLineNos.next();
			List<Integer> list = needToRemoveLineNosOfFile.get(key);
			Set needToRemoveLineNos = new HashSet<>(list);
			System.out.println("needToRemoveLineNos： " + key);
			System.out.println(needToRemoveLineNos);
		}
		return needToRemoveLineNosOfFile;
	}

	public  MultiValueMap<String, String> orgnizeReport(List<Map> readReport, String rootPath) {
		MultiValueMap<String, String> fileMthodAndPos = new LinkedMultiValueMap<>(); // 文件里面的方法名和行号
		MultiValueMap<String, String> fileMthods = new LinkedMultiValueMap<>();// 文件里面的方法名
		for (Iterator iterator = readReport.iterator(); iterator.hasNext();) {
			Map map = (Map) iterator.next();
			String filePath = rootPath + map.get(RELATIVE_FILE_PATH);
			File file = new File(filePath);
			String methodName = (String) map.get(METHOD_NAME);
			String lineNo = (String) map.get(LINE_NO);
			String elementType = (String) map.get(ELEMENT_TYPE);
			fileMthodAndPos.add(filePath, Joiner.on("$").join(methodName,lineNo,elementType));
			fileMthods.add(filePath, methodName);
		}
		// 遍历每一个文件的所有方法,打印重复名称的方法
		System.out.println("Duplicate name Method: ");
		Iterator<String> iterator = fileMthods.keySet().iterator();
		while (iterator.hasNext()) {
			String key = (String) iterator.next();
			List<String> list = fileMthods.get(key);
			HashBag methodBag = new HashBag(list);
			HashSet methodSet = new HashSet(list);
			for (Iterator iterator2 = methodSet.iterator(); iterator2.hasNext();) {
				Object object = (Object) iterator2.next();
				int count = methodBag.getCount(object);
				if (count > 1) {
					System.out.println(object);
				}
			}
		}
		return fileMthodAndPos;
	}

	public static List<Integer> findNeedToRemoveLineNosOfMethod(File file, String encoding, String methodName, final String lineNo, final String elemtnType) throws Exception {
		// System.out.println(file.getName());
		
		if(elemtnType.equals(ELEMENT_TYPE_CONSTANT)){
			return new ArrayList<Integer>();
		}
		
		//一下元素类型是 Method
		List<String> contents = null;
		try {
			contents = Files.readLines(file, Charsets.UTF_8);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (CollectionUtils.isEmpty(contents)) {
			return Collections.EMPTY_LIST;
		}
		// 查找指定方法的行数 !
		final String boundryRegx = ".*\\b" + methodName + "\\b.*"; // 精确匹配方法名
		List<Integer> needRemoveLineNoList = Files.readLines(file, Charsets.UTF_8, new LineProcessor<List<Integer>>() {
			int currentLineNo = 0; // start at 1
			List<Integer> methodLines = Lists.newArrayList();
			Stack<String> curlyBrackets = new Stack<>();
			public boolean processLine(String line) {
				currentLineNo++;
				if(elemtnType.equals(ELEMENT_TYPE_METHOD)){
					// 开始找指定方法，并且开始记录行号
					if (methodLines.isEmpty()) {
						if (line.matches(boundryRegx) && lineNo.equals(String.valueOf(currentLineNo))) {
							methodLines.add(currentLineNo);
							// 开始匹配左花括号
							int countMatchesLeft = StringUtils.countMatches(line, "{");
							for (int i = 0; i < countMatchesLeft; i++) {
								curlyBrackets.push("{");
							}
						}
					} else {
						methodLines.add(currentLineNo);
						// 开始匹配左花括号
						int countMatchesLeft = StringUtils.countMatches(line, "{");
						for (int i = 0; i < countMatchesLeft; i++) {
							curlyBrackets.push("{");
						}
						// 开始匹配右花括号,需保证堆栈里面已经有左花括号
						if(!curlyBrackets.isEmpty()){
							int countMatchesRight = StringUtils.countMatches(line, "}");
							for (int i = 0; i < countMatchesRight; i++) {
									curlyBrackets.pop();
							}
							if (curlyBrackets.isEmpty()) {
								return false; //跳出处理
							}
						}
					}
				}

				return true;
			}

			public List<Integer> getResult() {
				return methodLines;
			}
		});
		// 查找指定方法的注释的行数
		if (CollectionUtils.isNotEmpty(needRemoveLineNoList)) {
			Integer methodLineNo = needRemoveLineNoList.get(0);
			while (true) {
				methodLineNo--;
				String previousString = contents.get(methodLineNo - 1); // list index start at 0
				if (StringUtils.isNotBlank(previousString)) {
					boolean containsNone = StringUtils.containsNone(previousString, "*/\\@");
					boolean containsA = StringUtils.contains(previousString, "}");
					boolean containsB = StringUtils.contains(previousString, ";");
					if (containsNone && (containsA || containsB)) {
						break;
					}
				}
				needRemoveLineNoList.add(methodLineNo);
			}
		}
		// System.out.println(needRemoveLineNoList);
		return needRemoveLineNoList;
	}

	public static void removeByLineNoList(List<String> contents, final Set<Integer> lineNoList, File file, String encoding) throws IOException {
		Collection<String> filterList = Collections2.filter(contents, new Predicate<String>() {
			int lineCount = 0;

			@Override
			public boolean apply(String input) {
				lineCount++;
				if (lineNoList.contains(lineCount)) {
					return false;
				}
				return true;
			}
		});
		FileUtils.writeLines(file, encoding, filterList, false);
	}

	public static List<Map> readReport(File file, String encoding, String rootPath) throws IOException {
		List<Map> result = new ArrayList<>();
		String rawContent = FileUtils.readFileToString(file, encoding);
		String[] split = rawContent.split("\r\n|\r|\n");
		System.out.println("长度:" + split.length);
		
		//匹配方法信息
		Pattern regex = Pattern.compile("(com\\.[^(]+)\\(.*\\.java:([^)]+)\\)\\s+("+ELEMENT_TYPE_METHOD+")");
		Matcher matcher = regex.matcher(rawContent);
		while (matcher.find()) {
			Map map = new HashMap<>();
			// String fullName = matcher.group(0);
			//// System.out.println(fullName);
			// String fullName1 = matcher.group(1);
			// System.out.println("1");
			// System.out.println(fullName1);
			// String fullName2 = matcher.group(2);
			// System.out.println("2");
			// System.out.println(fullName2);
			// fullName
			System.out.println("Full Qualified Name: ");
			String fullName = matcher.group(1);
			map.put(FULL_NAME, fullName);
			System.out.println(fullName);
			// method name
			System.out.println("Method Name: ");
			String methodName = fullName.substring(fullName.lastIndexOf(".") + 1, fullName.length());
			System.out.println(methodName);
			map.put(METHOD_NAME, methodName);
			// relative path
			String filePath = File.separator + fullName.substring(0, fullName.lastIndexOf(".")).replaceAll("\\.", File.separator) + ".java";
			System.out.println(filePath);
			map.put(RELATIVE_FILE_PATH, filePath);
			// line no.
			String lineNo = matcher.group(2);
			System.out.println(lineNo);
			map.put(LINE_NO, lineNo);
			// element type 
			String elementType = matcher.group(3);
			System.out.println(elementType);
			map.put(ELEMENT_TYPE, elementType);
			
			result.add(map);
			System.out.println("");
		}
		return result;
	}
}
